<template>
  <view class="nut-elevator">
    <nut-scroll-view
      ref="listview"
      class="nut-elevator__list nut-elevator__list--mini"
      :scroll-top="scrollTop"
      :scroll-y="true"
      :scroll-with-animation="true"
      :scroll-anchoring="true"
      :style="{ height: isNaN(+height) ? height : `${height}px` }"
      @scroll="listViewScroll"
    >
      <view
        v-for="(item, index) in indexList"
        :key="item[acceptKey]"
        :ref="setListGroup"
        :class="['nut-elevator__list__item', `elevator__item__${index}`]"
      >
        <view class="nut-elevator__list__item__code">{{ item[acceptKey] }}</view>
        <view
          v-for="subitem in item.list"
          :key="subitem['id']"
          class="nut-elevator__list__item__name"
          :class="{
            'nut-elevator__list__item__name--highcolor': currentData.id === subitem.id && currentKey === item[acceptKey]
          }"
          @click="handleClickItem(item[acceptKey], subitem)"
        >
          <span v-if="!$slots.default" v-html="subitem.name"></span>
          <slot v-else :item="subitem"></slot>
        </view>
      </view>
    </nut-scroll-view>
    <view v-show="scrollY > 2 && isSticky" class="nut-elevator__list__fixed">
      <view class="nut-elevator__list__fixed-title">{{ indexList?.[currentIndex]?.[acceptKey] }}</view>
    </view>
    <view v-show="scrollStart" v-if="indexList.length > 0" class="nut-elevator__code--current">
      {{ indexList?.[codeIndex]?.[acceptKey] }}
    </view>
    <view class="nut-elevator__bars" @touchstart="touchStart" @touchmove.stop.prevent="touchMove" @touchend="touchEnd">
      <view class="nut-elevator__bars__inner">
        <view
          v-for="(item, index) in indexList"
          :key="item[acceptKey]"
          class="nut-elevator__bars__inner__item"
          :class="{ active: item[acceptKey] === indexList[currentIndex][acceptKey] }"
          :data-index="index"
          @click="handleClickIndex(item[acceptKey])"
        >
          {{ item[acceptKey] }}
        </view
        >
      </view>
    </view>
  </view>
</template>
<script lang="ts">
import { computed, reactive, toRefs, nextTick, ref, Ref, watch, PropType } from 'vue'
import { createComponent } from '@/packages/utils/create'
import { ElevatorData } from './type'
const { create } = createComponent('elevator')

import Taro from '@tarojs/taro'
import NutScrollView from '../scroll-view/index.taro.vue'

export default create({
  components: {
    NutScrollView
  },
  props: {
    height: {
      type: [Number, String],
      default: '200px'
    },
    acceptKey: {
      type: [String],
      default: 'title'
    },
    indexList: {
      type: Array as PropType<any[]>,
      default: () => {
        return []
      }
    },
    isSticky: {
      type: [Boolean],
      default: false
    },
    spaceHeight: {
      type: [Number],
      default: 23
    },
    titleHeight: {
      type: [Number],
      default: 35
    }
  },
  emits: ['clickItem', 'clickIndex', 'change'],
  setup(props, { emit, expose }) {
    const spaceHeight = 23
    const listview: Ref<HTMLElement> = ref() as Ref<HTMLElement>
    const state = reactive({
      anchorIndex: 0,
      codeIndex: 0,
      listHeight: [] as number[],
      listGroup: [] as HTMLLIElement[],
      touchState: {
        y1: 0,
        y2: 0
      },
      scrollStart: false,
      currentIndex: 0,
      query: Taro.createSelectorQuery(),
      scrollTop: 0,
      currentData: {} as ElevatorData,
      currentKey: '',
      scrollY: 0
    })

    const clientHeight = computed(() => {
      return listview.value.clientHeight
    })

    const fixedStyle = computed(() => {
      return {
        height: `${state.listHeight[state.listGroup.length - 1]}px`
      }
    })

    const getData = (el: HTMLElement): string | void => {
      if (!el.dataset.index) {
        return '0'
      }
      return el.dataset.index as string
    }

    const setListGroup = (el: any) => {
      nextTick(() => {
        if (!state.listGroup.includes(el) && el != null) {
          state.listGroup.push(el)
        }
      })
    }

    const calculateHeight = () => {
      state.listHeight = []
      let height = 0
      state.listHeight.push(height)
      for (let i = 0; i < state.listGroup.length; i++) {
        state.query.selectAll(`.elevator__item__${i}`).boundingClientRect()
        state.query.exec((res) => {
          height += Math.floor(res[i][0].height)
          state.listHeight.push(height)
        })
      }
    }

    const scrollTo = (index: number) => {
      if (!index && index !== 0) {
        return
      }
      if (index < 0) index = 0
      if (index > state.listHeight.length - 2) index = state.listHeight.length - 2
      state.codeIndex = index
      state.scrollTop = state.listHeight[index]
    }

    const touchStart = (e: TouchEvent) => {
      state.scrollStart = true
      let index = getData(e.target as HTMLElement)
      let firstTouch = e.touches[0]
      state.touchState.y1 = firstTouch.pageY
      state.anchorIndex = +index
      state.codeIndex = +index
      scrollTo(+index)
    }

    const touchMove = (e: TouchEvent) => {
      let firstTouch = e.touches[0]
      state.touchState.y2 = firstTouch.pageY
      let delta = ((state.touchState.y2 - state.touchState.y1) / spaceHeight) | 0
      state.codeIndex = state.anchorIndex + delta
      scrollTo(state.currentIndex)
    }

    const touchEnd = () => {
      state.scrollStart = false
    }

    const handleClickItem = (key: string, item: ElevatorData) => {
      emit('clickItem', key, item)
      state.currentData = item
      state.currentKey = key
    }

    const handleClickIndex = (key: string) => {
      emit('clickIndex', key)
    }

    const listViewScroll = (e: Event) => {
      let target = e.target as Element
      let scrollTop = target.scrollTop
      const listHeight = state.listHeight
      state.scrollY = Math.floor(scrollTop)
      for (let i = 0; i < listHeight.length - 1; i++) {
        let height1 = listHeight[i]
        let height2 = listHeight[i + 1]
        if (state.scrollY >= height1 && state.scrollY < height2) {
          state.currentIndex = i
          return
        }
      }
    }

    expose({
      scrollTo
    })

    watch(
      () => state.listGroup.length,
      () => {
        Taro.nextTick(calculateHeight)
      }
    )

    watch(
      () => state.currentIndex,
      (newVal: number) => {
        emit('change', newVal)
      }
    )

    return {
      ...toRefs(state),
      clientHeight,
      fixedStyle,
      setListGroup,
      listview,
      touchStart,
      touchMove,
      touchEnd,
      handleClickItem,
      handleClickIndex,
      listViewScroll
    }
  }
})
</script>
